package com.dennytech.tac.widget;

import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

import com.dennytech.tac.net.HttpClientFactory;
import com.dennytech.tac.net.HttpClientFactory.MyHttpClient;
import com.dennytech.tac.utils.DefaultLockManager;
import com.dennytech.tac.utils.LockManager;
import com.dianping.memcache.DefaultMemCache;
import com.google.android.photostream.ImageTask;
import com.dennytech.tac.R;

public class NetworkImageView extends ImageView {
	private static final String LOG_TAG = NetworkImageView.class.getSimpleName();
	
	public static final int MEM_CACHE_SIZE = 240 * 0x400; // 240k
	public static final int MEM_CACHE_LIFETIME = -1; // unlimited

	protected String url;
	public boolean filterBitmap;
	public boolean requireBeforeAttach;
	public int placeholderEmpty, placeholderLoading, placeholderError;

	protected boolean currentPlaceholder;
	protected boolean hasSavedScaleType;
	protected ScaleType savedScaleType;
	protected boolean attached;
	protected Boolean imageRetrieve;
	private LoadTask currentTask;

	public NetworkImageView(Context context) {
		this(context, null);
	}

	public NetworkImageView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public NetworkImageView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		TypedArray a = context.obtainStyledAttributes(attrs,
			R.styleable.NetworkImageView);
		filterBitmap = a.getBoolean(R.styleable.NetworkImageView_filterBitmap,
			true);
		requireBeforeAttach = a.getBoolean(
			R.styleable.NetworkImageView_requireBeforeAttach, false);
		placeholderEmpty = a.getResourceId(
			R.styleable.NetworkImageView_placeholderEmpty, 0);
		placeholderLoading = a.getResourceId(
			R.styleable.NetworkImageView_placeholderLoading, 0);
		placeholderError = a.getResourceId(
			R.styleable.NetworkImageView_placeholderError, 0);
		a.recycle();
	}

	protected LoadTask createLoadTask() {
		return new LoadTask(this);
	}

	public void setLocalBitmap(Bitmap bmp) {
		setImageBitmap(bmp);

		url = "local_bitmap";
		imageRetrieve = true;
	}

	public void setDrawable(Drawable d, boolean isPlaceholder) {
		if (currentPlaceholder != isPlaceholder) {
			if (isPlaceholder) {
				if (!hasSavedScaleType) {
				    savedScaleType = getScaleType();
				    hasSavedScaleType = true;
				}
				setScaleType(ScaleType.CENTER_INSIDE);
			} else {
				if (hasSavedScaleType) {
				    setScaleType(savedScaleType);
				}
			}
		}
		currentPlaceholder = isPlaceholder;
		setImageDrawable(d);
		if (isPlaceholder && d instanceof AnimationDrawable) {
			((AnimationDrawable) d).start();
		}
	}

	protected Map<String, Bitmap> memcache() {
		return memcache;
	}

	protected boolean require() {
		if (!(attached || requireBeforeAttach)) {
			return false;
		}
		if (url == null) {
			Drawable d = placeholderEmpty == 0 ? null
					: getResources().getDrawable(placeholderEmpty);
			setDrawable(d, true);
			imageRetrieve = true;
			return true;
		}
		if (url != null && imageRetrieve == null) {
			//
			// check memcache first
			//
			Bitmap bmp = memcache().get(url);
			if (bmp == null) {
				currentTask = createLoadTask();
				currentTask.execute(url);
				Drawable d = placeholderLoading == 0 ? null
						: getResources().getDrawable(placeholderLoading);
				setDrawable(d, true);
				imageRetrieve = false;
			} else {
				BitmapDrawable d = new BitmapDrawable(bmp);
				d.setFilterBitmap(filterBitmap);
				setDrawable(d, false);
				imageRetrieve = true;
			}
			return true;
		}
		return false;
	}

	protected boolean discard() {
		if (url != null && Boolean.FALSE.equals(imageRetrieve)) {
			if (currentTask != null) {
				currentTask.cancel(true);
				currentTask = null;
			}
			imageRetrieve = null;
			return true;
		}
		return false;
	}

	public void setImage(String url) {
		Log.i(LOG_TAG, "set image with url: " + url);
		
		if (url != null && url.length() == 0)
			url = null;
		if (url == null && this.url == null)
			return;
		if (url != null && url.equals(this.url))
			return;
		discard();
		imageRetrieve = null;
		this.url = url;
		require();
		if (imageRetrieve == null) {
			setImageDrawable(null);
		}
	}

	@Override
	protected void onAttachedToWindow() {
		super.onAttachedToWindow();
		attached = true;
		require();
	}

	@Override
	protected void onDetachedFromWindow() {
		super.onDetachedFromWindow();
		attached = false;
		discard();
	}

	@Override
	protected void onWindowVisibilityChanged(int visibility) {
		super.onWindowVisibilityChanged(visibility);
		if (visibility == View.VISIBLE) {
			attached = true;
			require();
		} else {
			attached = false;
			discard();
		}
	}

	public final static LockManager<String> urlLocks = new DefaultLockManager<String>(
			10);
	public final static DefaultMemCache<String, Bitmap> memcache = new DefaultMemCache<String, Bitmap>(
			MEM_CACHE_SIZE, MEM_CACHE_LIFETIME) {
		@Override
		protected int sizeOf(Object object) {
			if (object instanceof Bitmap) {
				Bitmap bmp = (Bitmap) object;
				return bmp.getRowBytes() * bmp.getHeight();
			} else {
				return super.sizeOf(object);
			}
		}
	};

	protected static class LoadTask extends ImageTask<String, Void, Bitmap> {
		WeakReference<NetworkImageView> parent;
		HttpGet get;

		public LoadTask(NetworkImageView p) {
			parent = new WeakReference<NetworkImageView>(p);
		}

		@Override
		public Bitmap preDoInBackground(String... params) {
			final String url = params[0];

			//
			// check memcache first
			//
			return memcache.get(url);
		}

		@Override
		public Bitmap doInBackground(String... params) {
			final String url = params[0];
			urlLocks.lock(url);
			MyHttpClient httpClient = null;
			try {
				if (isCancelled())
					return null;

				//
				// download from network
				//
				httpClient = HttpClientFactory.getHttpClient();
				HttpGet get = new HttpGet(url);
				this.get = get;
//				HttpHost proxy = DPEnv.agent().globalProxy();
//				ConnRouteParams.setDefaultProxy(get.getParams(), proxy);
				HttpResponse response = httpClient.execute(get);
				HttpEntity entity = response.getEntity();
				System.gc();
				Bitmap bitmap = null;
				if (this.get == get
						&& response.getStatusLine().getStatusCode() / 100 == 2) {
					InputStream ins = entity.getContent();
					try {
						bitmap = BitmapFactory.decodeStream(ins);
					} catch (Exception e) {
						System.gc();
					}
					ins.close();
				}
				entity.consumeContent();
				if (this.get != get)
					return null;
				else if (bitmap == null)
					return null;
				else {
					NetworkImageView view = parent.get();
					int width = view.getWidth();
					int height = view.getHeight();
					if ((width > 0) && (height > 0)) {
					    bitmap = Bitmap.createScaledBitmap(bitmap, width, height, view.filterBitmap);
					}
					memcache.put(url, bitmap);
					return bitmap;
				}
			} catch (Exception e) {
				return null;
			} catch (Error e) {
				return null;
			} finally {
				this.get = null;
				if (httpClient != null)
					httpClient.recycle();
				urlLocks.unlock(url);
			}
		}

		@Override
		public void onCancelled() {
			super.onCancelled();
			HttpGet get = this.get;
			if (get != null) {
				try {
					get.abort();
					get = null;
				} catch (Exception e) {
				}
			}
		}

		@Override
		public void onPostExecute(Bitmap result) {
			NetworkImageView view = parent.get();
			if (view == null)
				return;
			if (Boolean.FALSE.equals(view.imageRetrieve)
					&& this == view.currentTask) {
				if (result == null) {
					Drawable d = view.placeholderError == 0 ? null
							: view.getResources().getDrawable(
								view.placeholderError);
					view.setDrawable(d, true);
				} else {
					BitmapDrawable d = new BitmapDrawable(result);
					d.setFilterBitmap(view.filterBitmap);
					view.setDrawable(d, false);
				}
				view.imageRetrieve = true;
				view.currentTask = null;
			}
		}
	}
}
